﻿using System;
using System.Collections.Generic;

namespace CoreLibrary {

    public sealed class Asserts {
        
        public static void NotNull(object obj) {
            NotNull(obj, "[Assertion failed] - Object can not be null.");
        }

        public static void NotNull(object obj, string message) {
            NotNull<ArgumentException>(obj, message);
        }

        public static void NotNull<T>(object obj, string message) where T : Exception {
            if (obj == null) {
                Fail<T>(message);
            }
        }

        public static void NotEmpty<TC>(ICollection<TC> collection) {
            NotEmpty<ArgumentException, TC>(collection, "[Assertion failed] - Collection can not be emtpy.");
        }

        public static void NotEmpty<TC>(ICollection<TC> collection, string message) {
            NotEmpty<ArgumentException, TC>(collection, message);
        }

        public static void NotEmpty<T, TC>(ICollection<TC> collection, string message) where T : Exception {
            if (CollectionUtil.IsEmpty(collection)) {
                Fail<T>(message);
            }
        }

        public static void NotBlank(string s) {
            NotBlank(s, "[Assertion failed] - String can not be empty.");
        }

        public static void NotBlank(string s, string message) {
            NotBlank<ArgumentException>(s, message);
        }

        public static void NotBlank<T>(string s, string message) where T : Exception {
            if (string.IsNullOrEmpty(s)) {
                Fail<T>(message);
            }
        }

        public static void Equals(int expected, int actual) {
            Equals<ArgumentException>(expected, actual, "value must be equal.");
        }

        public static void Equals(string expected, string actual) {
            Equals<ArgumentException>(expected, actual, "value must be equal.");
        }

        public static void Equals<T>(object expected, object actual, string message) where T : Exception {
            if (!expected.Equals(actual)) {
                 Fail<T>(message);
            }
        }

        public static void NotEquals(object expected, object actual) {
            NotEquals<ArgumentException>(expected, actual, "value must not be equal.");
        }

        public static void NotEquals<T>(object expected, object actual, string message) where T : Exception {
            if (ReferenceEquals(expected, actual) || expected.Equals(actual)) {
                Fail<T>(message);
            }
        }

        public static void False(bool value) {
            False(value, "Value must be false");
        }

        public static void False(bool value, string message) {
            False<ArgumentException>(value, message);
        }

        public static void False<T>(bool? value, string message) where T : Exception {
            if (value != false) {
                Fail<T>(message);
            }
        }

        public static void True(bool value) {
            True(value, "Value must be true.");
        }

        public static void True(bool value, string message) {
            True<ArgumentException>(value,message);
        }

        public static void True<T>(bool value, string message) where T : Exception {
            if (value != true) {
                Fail<T>(message);
            }
        }

        private static void Fail<T>(string message) where T : Exception {
            var type = typeof(T);
            var constructor = type.GetConstructor( new[]{
                typeof(string)
            });

            if (constructor == null) {
                throw new ArgumentException("Class[" + type.FullName + "] must have a constructor of a single string parameter");
            }

            throw (Exception) constructor.Invoke(new object[] {
                   message
            });
        }

    }

}